#pragma once
#include <iostream>
#include <sstream>
#include <unordered_map>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <memory>
#include <unistd.h>
const std::string Sep = "\r\n";
const std::string LineSep = ": ";
const std::string Blank = " ";
const std::string BlankLine = "\r\n";
std::string defaultversion = "HTTP/1.0";
const std::string notfound_dir = "wwwroot/notfound.html";
// int isredirect = 1;
bool PraseOneLine(std::string &req, std::string *line, const std::string &sep)
{
    auto pos = req.find(sep);
    if (pos == std::string::npos)
    {
        return false;
    }
    *line = req.substr(0, pos);
    req.erase(0, pos + sep.size());
    return true;
}

class HttpRequest
{
public:
    bool IsHasArgs()
    {
        return !_args.empty();
    }
    void GetSessionId()
    {
        std::string prefix = "Cookie: ";
        for(auto& line:_req_header)
        {
            if(strncmp(line.c_str(),prefix.c_str(),prefix.size())==0)
            {
                std::string cookie = line.substr(prefix.size());
                _cookies.push_back(cookie);
            }
        }
        prefix = "sessionid=";
        for(auto& cookie:_cookies)
        {
            if(strncmp(cookie.c_str(),prefix.c_str(),prefix.size())==0)
            {
                _sessionid = cookie.substr(prefix.size());
                break;
            }
        }
    }
    bool Deserialize(std::string &req)
    {
        // std::string line;
        // 1.获得请求行
        if (!PraseOneLine(req, &_req_line, Sep))
        {
            // std::cout<<"解析请求行失败"<<std::endl;
            return false;
        }
        // std::cout<<"解析命令行成功: "<<_req_line<<std::endl;

        //  解析请求行
        PraseRequestLine();
        // 2.获得请求报头
        while (true)
        {
            std::string line;
            bool ret = PraseOneLine(req, &line, Sep);

            // 不是空行
            if (ret && !line.empty())
            {
                _req_header.push_back(line);
            }
            else if (ret && line.empty()) // 空行
            {
                break;
            }
            else
            {
                break;
            }
        }
        GetSessionId();
        // 解析k v
        SplitKV();
        // 3.获得请求正文
        _req_body = req;
        // 解析参数
        if (_method == "GET")
        {
            auto pos = _method.find("?");
            if (pos != std::string::npos)
            {
                _args = _method.substr(pos + 1);
            }
        }
        else if (_method == "POST")
        {
            _args = _req_body;
        }
        return true;
    }
    void Print()
    {
        std::cout << _req_line << std::endl;
        std::cout << "method: " << _method << std::endl;
        std::cout << "url: " << _url << std::endl;
        std::cout << "version: " << _version << std::endl;
        std::cout << "args:" << _args << std::endl;
        for (auto &e : _kv)
        {
            std::cout << e.first << ": " << e.second << std::endl;
        }

        std::cout << BlankLine;
        //std::cout << _req_body << std::endl;
    }
    std::string Url()
    {
        return _url;
    }

private:
    bool PraseRequestLine()
    {
        // std::stringstream ss(_req_line);
        // ss >> _method >> _url >> _version;
        std::string req_line = _req_line;
        auto pos = req_line.find(Blank);
        _method = req_line.substr(0, pos);
        req_line.erase(0, pos + Blank.size());

        pos = req_line.find(Blank);
        _url = req_line.substr(0, pos);
        req_line.erase(0, pos + Blank.size());

        _version = req_line;
        return true;
    }
    bool SplitKV()
    {
        for (auto &e : _req_header)
        {
            auto pos = e.find(LineSep);
            std::string key, val;
            key = e.substr(0, pos);
            val = e.substr(pos + LineSep.size());
            _kv.insert(make_pair(key, val));
        }
        return true;
    }
    std::string Method()
    {
        return _method;
    }
    std::string Body()
    {
        return _req_body;
    }
public:
    std::string Args()
    {
        return _args;
    }
    std::string SessionId()
    {
        return _sessionid;
    }
    void SetSessionId(const std::string& sessionid)
    {
        _sessionid = sessionid;
    }
private:
    std::string _req_line;
    std::vector<std::string> _req_header;
    std::string _req_body;
    // 跟详细的信息
    std::string _method;
    std::string _url;
    std::string _version;

    std::unordered_map<std::string, std::string> _kv;
    std::string _args;

    std::vector<std::string> _cookies;
    std::string _sessionid;
};

class HttpResponse
{
public:
    HttpResponse(std::string version = defaultversion) : _version(version)
    {
    }
    void Redirect()
    {
        // AddHeader("Location", "https://www.qq.com");
        AddHeader("Location", "http://1.94.214.183:8080/register.html");

        std::string resp_str;
        _status_code = 302;
        SetStatusDesc();
        // resp_str = _version + Blank + std::to_string(_status_code) + Blank + _status_desc + Sep;
        // resp_str += _resp_header;
        // resp_str += BlankLine;
        // return resp_str;
        return;
    }
    void BuildResp(std::string& url)
    {
        // 重定向
        if (url.find("/redirect-me") != std::string::npos)
        {
            // 重定向到新的URL，这里用硬编码作为示例
            Redirect();
            return;
        }

        // std::string out;
        GetContent(url, &_resp_body);
        if (_resp_body.empty())
        {
            url = notfound_dir;
            GetContent(url, &_resp_body);
            _status_code = 404;
        }
        else
        {
            _status_code = 200;
        }
        SetStatusDesc();

        // Content-Length
        if (_resp_body.size() != 0)
        {
            AddHeader("Content-Length", std::to_string(_resp_body.size()));
        }
        std::string suffix = Suffix(url);
        std::string suffix_desc = SuffixToDesc(suffix);
        AddHeader("Content-Type", suffix_desc);
    }
    std::string Serialize(std::string url, HttpRequest &request)
    {
        // // 重定向
        // if (url.find("/redirect-me") != std::string::npos)
        // {
        //     // 重定向到新的URL，这里用硬编码作为示例
        //     return Redirect();
        // }

        // // std::string out;
        // GetContent(url, &_resp_body);
        // if (_resp_body.empty())
        // {
        //     url = notfound_dir;
        //     GetContent(url, &_resp_body);
        //     _status_code = 404;
        // }
        // else
        // {
        //     _status_code = 200;
        // }
        // SetStatusDesc();

        std::string resp_str;
        resp_str = _version + Blank + std::to_string(_status_code) + Blank + _status_desc + Sep;
        // // Content-Length
        // if (_resp_body.size() != 0)
        // {
        //     AddHeader("Content-Length", std::to_string(_resp_body.size()));
        // }
        // std::string suffix = Suffix(url);
        // std::string suffix_desc = SuffixToDesc(suffix);
        // AddHeader("Content-Type", suffix_desc);
        resp_str += _resp_header;
        resp_str += BlankLine;
        resp_str += _resp_body;
        return resp_str;
    }

private:
    // 获得后缀比如: .html  .jpg ......
    std::string Suffix(std::string &url)
    {
        auto pos = url.rfind('.');
        if (pos != std::string::npos)
        {
            return url.substr(pos);
        }
        return "";
    }
    std::string SuffixToDesc(std::string &suffix)
    {
        if (suffix == ".html")
        {
            return "text/html";
        }
        else if (suffix == ".jpg")
        {
            return "application/x-jpg";
        }
        else
            return "text/html"; // 没有就默认
    }
public:
    void AddHeader(const std::string &key, const std::string &val)
    {
        _resp_header += (key + LineSep + val + Sep);
    }

    void GetContent(const std::string &url, std::string *out)
    {
        std::ifstream in(url, std::ios::binary);
        if (!in.is_open())
        {
            return;
        }

        in.seekg(0, in.end);
        int filesize = in.tellg();
        in.seekg(0, in.beg);
        out->resize(filesize);
        in.read((char *)out->c_str(), filesize);
        in.close();
        // int sockfd = open(url.c_str(), O_RDONLY);
        // if (sockfd < 0)
        // {
        //     _status_code = 404;
        //     SetStatusDesc();
        //     //url+="/notfound.html";
        //     int sockfd = open(notfound_dir.c_str(), O_RDONLY);
        //     if (sockfd < 0)
        //     {
        //         std::cout << "open notfound error" << std::endl;
        //         return;
        //     }
        //     char buffer[1024];
        //     while (true)
        //     {
        //         int n = read(sockfd, buffer, sizeof(buffer) - 1);
        //         if (n > 0)
        //         {
        //             buffer[n] = 0;
        //             *out += buffer;
        //         }
        //         else
        //         {
        //             break;
        //         }
        //     }
        //     return;
        // }
        // char buffer[1024];
        // while (true)
        // {
        //     int n = read(sockfd, buffer, sizeof(buffer) - 1);
        //     if (n > 0)
        //     {
        //         buffer[n] = 0;
        //         *out += buffer;
        //     }
        //     else
        //     {
        //         break;
        //     }
        // }
        // _status_code = 200;
        // SetStatusDesc();
    }
    void SetStatusDesc()
    {
        switch (_status_code)
        {
        case 200:
            _status_desc = "OK";
            break;
        case 404:
            _status_desc = "Not Found";
            break;
        case 302:
            _status_desc = "Found";
        default:
            // ToDo
            break;
        }
    }
public:
    void SetBody(std::string& s)
    {
        _resp_body = s;
    }
    void SetCode(int code)
    {
        _status_code = code;
        SetStatusDesc();
    }
private:
    std::string _version;
    int _status_code;
    std::string _status_desc;
    std::string _resp_header;
    std::string _resp_body;
};